<?php

namespace Sys\Helper;
/**
 * 微信发放红包辅助类
 * User: Administrator
 * 参考文档:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
 * Date: 2018/1/25
 * Time: 10:11
 */
use Exception;
class WxRedHelper
{
    private $config
        = [
            'red_url' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack',//发放URL
        ];

    /**
     * WxRedHelper constructor.
     * @param $config array 设置参数
     * @throws Exception
     */
    public function __construct($config)
    {
        if (!isset($config['mch_app_key'], $config['mch_id'], $config['wxappid'], $config['cert_path'], $config['key_path'])) {
            throw new Exception('mch_app_key, mch_id, wxappid, cert_path, key_path参数未设置');
        }
        $this->config['mch_app_key'] = $config['mch_app_key'];//商户的APP_ID
        $this->config['mch_id']      = $config['mch_id'];//商户号
        $this->config['wxappid']     = $config['wxappid'];//公众号ID
        $this->config['cert_path']   = $config['cert_path'];//证书路径
        $this->config['key_path']    = $config['key_path'];//证书路径
    }

    /**
     * 发送红包
     * @param $re_openid string openid
     * @param $total_amount float 金额,分
     * @param $send_name string 显示的名称
     * @param $wishing string 红包上的祝福语
     * @param $act_name string 署名
     * @param int $total_num 一次发多少个
     * @return mixed
     */
    public function send($re_openid, $total_amount, $send_name, $wishing, $act_name, $total_num = 1)
    {
        $params = [
            'nonce_str'    => $this->getNonce(32),
            'mch_id'       => $this->config['mch_id'],
            'wxappid'      => $this->config['wxappid'],
            'send_name'    => $send_name,
            're_openid'    => $re_openid,//用户的openid
            'total_amount' => $total_amount,
            'total_num'    => $total_num,
            'wishing'      => $wishing,
            'client_ip'    => $_SERVER['SERVER_ADDR'],
            'act_name'     => $act_name,

        ];
        if ($total_amount >= 200 * 100) {
            $params['scene_id'] = 'PRODUCT_2';//场景id
        }
        $params['mch_billno'] = $this->getBillno();//订单号
        $params['sign']       = $this->makeSign($params);
        $xml                  = $this->arrayToXml($params);
        $xml_result           = $this->httpCurl($this->config['red_url'], $xml);
        $result               = $this->xmlToArray($xml_result);
        return $result;
    }

    /**
     * arr 转xml
     * @param $arr
     * @return string
     */
    private function arrayToXml($arr)
    {
        $xml = '<xml>';
        foreach ($arr as $key => $val) {
            $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= '</xml>';
        return $xml;
    }

    /**
     * 作用：将xml转为array
     * @param $xml
     * @return mixed
     */
    private function xmlToArray($xml)
    {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    /**
     * 生成商户订单号
     * @return string
     */
    private function getBillno()
    {
        return $this->config['mch_id'] . date('Ymd') . $this->getNonce(10, 1);
    }

    /**
     * 创建签名
     * @param $params
     * @return bool|string
     */
    private function makeSign($params)
    {
        $string = $this->buildString($params);
        if (!$string) {
            return false;
        }
        $string .= '&key=' . $this->config['mch_app_key'];
        $md5    = md5($string);
        return strtoupper($md5);
    }


    /**
     * 拼接字符串
     * @param $params
     * @return bool|string
     */
    private function buildString($params)
    {
        if (!is_array($params)) {
            return false;
        }
        ksort($params);
        $string = '';
        foreach ($params as $key => $val) {
            if ($key != 'sign' && !empty($val)) {
                $string .= $key . '=' . $val . '&';
            }
        }
        $string = rtrim($string, '&');
        return $string;
    }

    /**
     * 生成随机数
     * @param int $length 默认长度为32
     * @param int $mode 1小写，2大写，3数字　4混合
     * @return string
     */
    private function getNonce($length = 32, $mode = 3)
    {

        switch ($mode) {
            case 1:
                $chars = '0123456789';
                break;
            case 2:
                $chars = 'abcdefghijklmnopqrstuvwxyz';
                break;
            default:
                $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
                break;
        }
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $str;
    }

    /**
     *  curl post 请求
     * @param $url
     * @param $vars
     * @param int $second
     * @param array $aHeader
     * @return mixed
     * @throws \Exception
     */
    private function httpCurl($url, $vars, $second = 30, $aHeader = array())
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        //以下两种方式需选择一种

        //第一种方法，cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->config['cert_path']);
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->config['key_path']);

        //第二种方式，两个文件合成一个.pem文件
//        curl_setopt($ch, CURLOPT_SSLCERT, getcwd() . '/all.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
        $data = curl_exec($ch);
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new Exception("call faild, errorCode:$error\n");
        }
    }
}